package com.example.back.service.impl;

import cn.hutool.core.util.IdcardUtil;
import com.alibaba.fastjson.JSONObject;
import com.example.back.dto.req.*;
import com.example.back.dto.resp.*;
import com.example.back.feign.FileFeign;
import com.example.back.jpa.ConfigRepository;
import com.example.back.jpa.DoctorBaseInfoRepository;
import com.example.back.jpa.ExInfoRepository;
import com.example.back.jpa.HospitalDepartmentRepository;
import com.example.back.jpa.PlantOrderRepository;
import com.example.back.jpa.RecommendRepository;
import com.example.back.mapper.DoctorMapper;
import com.example.back.service.DoctorService;
import com.example.common.entity.Config;
import com.example.common.entity.DoctorBaseInfo;
import com.example.common.entity.ExInfo;
import com.example.common.entity.HospitalDepartment;
import com.example.common.entity.PlantOrder;
import com.example.common.entity.Recommend;
import com.example.common.sendhttp.CaSendMessage;
import com.example.common.support.ApiException;
import com.example.common.support.Result;
import com.example.common.utils.CheckUtils;
import com.example.common.utils.IdUtils;
import com.example.common.utils.MapUtils;
import com.example.common.utils.RedisUtils;
import com.github.pagehelper.ISelect;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import lombok.extern.slf4j.Slf4j;
import org.javers.common.exception.JaversException;
import org.javers.spring.annotation.JaversAuditable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import cn.hutool.core.date.Month;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author wxy
 * @Date 2021/1/14 13:38
 * @Version 1.0
 */
@Slf4j
@Service
public class DoctorServiceImpl implements DoctorService {


    @Resource
    DoctorMapper doctorMapper;

    @Resource
    RecommendRepository recommendRepository;

    @Resource
    DoctorBaseInfoRepository doctorBaseInfoRepository;

    @Resource
    HospitalDepartmentRepository hospitalDepartmentRepository;

    @Resource
    ConfigRepository configRepository;

    @Resource
    FileFeign fileFeign;

    @Resource
    PlantOrderRepository plantOrderRepository;
    @Resource
    ExInfoRepository exInfoRepository;

    @Resource
    private CaSendMessage caSendMessage;

    @Resource
    private RedisUtils redisUtils;

    /**
     * 医生列表
     */
    @Override
    public PageInfo<DoctorListRsp> List(DoctorListReq doctorListReq) {

        PageInfo<DoctorListRsp> objectPageInfo = PageHelper.startPage(doctorListReq.getPage(), doctorListReq.getLimit()).doSelectPageInfo(() -> doctorMapper.List(doctorListReq));
        List<DoctorListRsp> list = objectPageInfo.getList();
        for (DoctorListRsp doctorListRsp : list) {
            Recommend recommend = recommendRepository.findByDcId(doctorListRsp.getDCID());
            if (ObjectUtils.isEmpty(recommend)) {
                doctorListRsp.setIsRecommend(false);
            } else {
                doctorListRsp.setIsRecommend(true);
                doctorListRsp.setSort(recommend.getSort());
            }
        }
        return objectPageInfo;
    }

    /**
     * 启用、禁用
     */
    @Override
    @JaversAuditable
    public Object DoDisable(DoDisableReq doDisableReq) {
        DoctorBaseInfo doctorBaseInfo = doctorBaseInfoRepository.findById(doDisableReq.getDCID()).orElse(null);
        if (doctorBaseInfo.getDisable()) {
            doctorBaseInfo.setDisable(false);
            doctorBaseInfoRepository.save(doctorBaseInfo);
        } else {
            //启用
            //新增判断电话不能重复
            if (doctorBaseInfoRepository.countByPhoneAndDisable(doctorBaseInfo.getPhone(), true) != 0) {
                throw new ApiException(400, "当前手机号已经存在");
            }
            doctorBaseInfo.setDisable(true);
            doctorBaseInfoRepository.save(doctorBaseInfo);
        }
        return null;
    }

    /**
     * 详情
     */
    @Override
    public DockerInfoRsp Info(DoDisableReq doDisableReq) {
        DockerInfoRsp dockerInfoRsp = new DockerInfoRsp();

        List<DepList> depListList = doctorMapper.findAllDepart();
        DoctorBaseInfo doctorBaseInfo = doctorBaseInfoRepository.findById(doDisableReq.getDCID()).orElse(null);

        Recommend recommend = recommendRepository.findByDcId(doDisableReq.getDCID());
        Config config = configRepository.findById(doDisableReq.getDCID()).orElse(null);
        //setDepList
        dockerInfoRsp.setDepList(depListList);
        RecommendInfo recommendInfo = new RecommendInfo();

        if (recommend != null) {
            if (ObjectUtils.isEmpty(recommend)) {
                recommendInfo.setIsRecommend(false);
            } else {
                recommendInfo.setSort(recommend.getSort());
                recommendInfo.setIsRecommend(true);
            }
            //setRecommend
            dockerInfoRsp.setRecommend(recommendInfo);
        }

        if (doctorBaseInfo != null) {
            HospitalDepartment hospitalDepartment = hospitalDepartmentRepository.findById(doctorBaseInfo.getDepartId()).orElse(null);
            Info info = new Info();
            info.setCertificateImg(doctorBaseInfo.getCertificateImg());
            info.setCertificateCode(doctorBaseInfo.getCertificateCode());
            info.setDepartID(doctorBaseInfo.getDepartId());
            info.setIntro(doctorBaseInfo.getIntro());
            info.setHead(doctorBaseInfo.getHead());
            info.setName(doctorBaseInfo.getName());
            info.setPhone(doctorBaseInfo.getPhone());
            info.setTitle(doctorBaseInfo.getTitle());
            info.setProfessionScope(doctorBaseInfo.getProfessionScope());
            info.setDepName(hospitalDepartment.getName());
            info.setIdUnm(doctorBaseInfo.getCardNum());
            //setInfo
            dockerInfoRsp.setInfo(info);
        }


        if (config != null) {
            ConfigInfo configInfoNetReferral = new ConfigInfo();
            configInfoNetReferral.setAType(10);
            configInfoNetReferral.setAName("在线复诊");
            configInfoNetReferral.setNotice(config.getReferralService());
            configInfoNetReferral.setPrice(config.getReferralPrice());
            configInfoNetReferral.setReception(config.getReferralReception());
            configInfoNetReferral.setValid(config.getReferralValid() / 60);
            configInfoNetReferral.setService(config.getNetReferral());

            ConfigInfo configInfoImg = new ConfigInfo();
            configInfoImg.setAType(11);
            configInfoImg.setAName("图文问诊");
            configInfoImg.setNotice(config.getImgTextService());
            configInfoImg.setPrice(config.getImgTextPrice());
            configInfoImg.setReception(config.getImgTextReception());
            configInfoImg.setValid(config.getImgTextValid() / 60);
            configInfoImg.setService(config.getImgText());

            ConfigInfo configInfoPhone = new ConfigInfo();
            configInfoPhone.setAType(12);
            configInfoPhone.setAName("电话问诊");
            configInfoPhone.setNotice(config.getPhoneService());
            configInfoPhone.setPrice(config.getPhonePrice());
            configInfoPhone.setReception(config.getPhoneReception());
            configInfoPhone.setValid(config.getPhoneValid());
            configInfoPhone.setService(config.getPhone());

            ConfigInfo configInfoVideo = new ConfigInfo();
            configInfoVideo.setAType(13);
            configInfoVideo.setAName("视频问诊");
            configInfoVideo.setNotice(config.getVideoService());
            configInfoVideo.setPrice(config.getVideoPrice());
            configInfoVideo.setReception(config.getVideoReception());
            configInfoVideo.setValid(config.getVideoValid());
            configInfoVideo.setService(config.getVideo());

            List<ConfigInfo> configInfos = new ArrayList<>();
            configInfos.add(configInfoNetReferral);
            configInfos.add(configInfoImg);
            configInfos.add(configInfoPhone);
            configInfos.add(configInfoVideo);
            //setConfig
            dockerInfoRsp.setConfig(configInfos);
        }


        return dockerInfoRsp;
    }

    /**
     * 上传文件
     */
    @Override
    public HashMap<String, Object> UpLoad(MultipartFile multipartFile) {
        Result<Object> result = fileFeign.updateFile(multipartFile);
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("UpSess", true);
        hashMap.put("url", result.getData());
        return hashMap;
    }

    /**
     * 保存
     */
    @Override
    @JaversAuditable
    public Object Save(DoctorReq doctorReq) {
        List<ConfigReqsSave> configReqs = doctorReq.getConfigReqs();
        // 保存返回的注册码
        Map<String, String> map = null;
        for (ConfigReqsSave configReq : configReqs) {
            if (BigDecimal.ZERO.compareTo(configReq.getPrice()) > 0) {
                throw new ApiException(400, "咨询价格不能为0，保存失败");
            }
            if (0 >= configReq.getValid()) {
                throw new ApiException(400, "咨询价格不能为0，保存失败");
            }

        }

        // 身份证校验
        CheckUtils.checkNull(doctorReq.getDoctor().getIdUnm(), new ApiException(1, "身份证为空"));
        if (!IdcardUtil.isValidCard18(doctorReq.getDoctor().getIdUnm())) {
            throw new ApiException(1, "身份证信息有误");
        }

        DoctorBaseInfo doctorBaseInfoDb = doctorBaseInfoRepository.findById(doctorReq.getDoctor().getDCID()).orElse(null);
        Doctor doctor = doctorReq.getDoctor();
        if (ObjectUtils.isEmpty(doctorBaseInfoDb)) {

            map = goToAddUser(doctorReq.getDoctor().getName(), doctorReq.getDoctor().getIdUnm(), doctorReq.getDoctor().getPhone());

            //新增判断电话不能重复
            if (doctorBaseInfoRepository.countByPhone(doctorReq.getDoctor().getPhone()) != 0) {
                throw new ApiException(400, "当前手机号已经存在");
            }
            Long dcid = IdUtils.generateId();
            DoctorBaseInfo doctorBaseInfo = new DoctorBaseInfo();

            doctorBaseInfo.setCaCode(map.get("msspId"));
            doctorBaseInfo.setDcId(dcid);
            doctorBaseInfo.setCreateTime(new Date());
            doctorBaseInfo.setCertificateCode(doctor.getCertificateCode());
            doctorBaseInfo.setCertificateImg(doctor.getCertificateImg());
            doctorBaseInfo.setDepartId(doctor.getDepartID());
            doctorBaseInfo.setHead(doctor.getHead());
            doctorBaseInfo.setIntro(doctor.getIntro());
            doctorBaseInfo.setName(doctor.getName());
            doctorBaseInfo.setPhone(doctor.getPhone());
            doctorBaseInfo.setProfessionScope(doctor.getProfessionScope());
            doctorBaseInfo.setTitle(doctor.getTitle());
            doctorBaseInfo.setDisable(false);
            doctorBaseInfo.setZprescription(true);
            doctorBaseInfo.setXprescription(true);
            doctorBaseInfo.setCardNum(doctorReq.getDoctor().getIdUnm());
            doctorBaseInfoRepository.save(doctorBaseInfo);

            if (!ObjectUtils.isEmpty(doctorReq.getRecommend())) {
                Recommend recommend = new Recommend();
                recommend.setSort(doctorReq.getRecommend().getSort());
                recommend.setCreateTime(new Date());
                recommend.setDcId(dcid);
                recommendRepository.save(recommend);
            }

            Config config = new Config();
            config.setDcId(dcid);
            for (ConfigReqsSave configReq : configReqs) {
                switch (configReq.getAType()) {
                    case 10:
                        config.setNetReferral(configReq.getService());
                        config.setReferralPrice(configReq.getPrice());
                        config.setReferralValid(configReq.getValid() * 60);
                        config.setReferralReception(configReq.getReception());
                        config.setReferralService(configReq.getNotice());
                        break;
                    case 11:
                        config.setImgText(configReq.getService());
                        config.setImgTextPrice(configReq.getPrice());
                        config.setImgTextValid(configReq.getValid() * 60);
                        config.setImgTextReception(configReq.getReception());
                        config.setImgTextService(configReq.getNotice());
                        break;
                    case 12:
                        config.setPhone(configReq.getService());
                        config.setPhonePrice(configReq.getPrice());
                        config.setPhoneValid(configReq.getValid());
                        config.setPhoneReception(configReq.getReception());
                        config.setPhoneService(configReq.getNotice());
                        break;
                    case 13:
                        config.setVideo(configReq.getService());
                        config.setVideoPrice(configReq.getPrice());
                        config.setVideoValid(configReq.getValid());
                        config.setVideoReception(configReq.getReception());
                        config.setVideoService(configReq.getNotice());
                        break;
                }
            }
            configRepository.save(config);

            ExInfo exInfo = new ExInfo();
            exInfo.setDcId(dcid);
            exInfo.setService(0);
            exInfo.setInquiry(0);
            exInfo.setPatient(0);
            exInfo.setPraise(new BigDecimal(100));
            exInfo.setPrescription(0);
            exInfo.setReceive(new BigDecimal(100));
            exInfoRepository.save(exInfo);

        } else {
            boolean changePhone = doctorBaseInfoDb.getPhone().equals(doctorReq.getDoctor().getPhone()) ? false : true;
            //新增判断电话不能重复
            if ((doctorBaseInfoRepository.countByPhone(doctorReq.getDoctor().getPhone()) > 0 && changePhone) || (doctorBaseInfoRepository.countByPhone(doctorReq.getDoctor().getPhone()) > 1 && !changePhone)) {
                throw new ApiException(400, "当前手机号已经存在");
            }
            // 判断是否修改身份证号
            if (ObjectUtils.isEmpty(doctorBaseInfoDb.getCardNum()) || !doctorBaseInfoDb.getCardNum().equals(doctorReq.getDoctor().getIdUnm())) {

                if (ObjectUtils.isEmpty(doctorBaseInfoDb.getCaCode())) {
                    map = goToAddUser(doctorReq.getDoctor().getName(), doctorReq.getDoctor().getIdUnm(), doctorReq.getDoctor().getPhone());
                    doctorBaseInfoDb.setCaCode(map.get("msspId"));
                } else {
                    // 删除ca 端的信息
                    String result = caSendMessage.deleteUser(doctorBaseInfoDb.getCaCode());
                    JSONObject jsonObject = JSONObject.parseObject(result);

                    if ("SUCCESS".equals(jsonObject.get("message"))) {
                        map = goToAddUser(doctorReq.getDoctor().getName(), doctorReq.getDoctor().getIdUnm(), doctorReq.getDoctor().getPhone());
                        doctorBaseInfoDb.setCaCode(map.get("msspId"));
                    } else {
                        Object message = jsonObject.get("message");
                        throw new ApiException(400, message.toString());
                    }
                }
            }
            doctorBaseInfoDb.setCertificateCode(doctor.getCertificateCode());
            doctorBaseInfoDb.setCertificateImg(doctor.getCertificateImg());
            doctorBaseInfoDb.setDepartId(doctor.getDepartID());
            doctorBaseInfoDb.setHead(doctor.getHead());
            doctorBaseInfoDb.setIntro(doctor.getIntro());
            doctorBaseInfoDb.setName(doctor.getName());
            doctorBaseInfoDb.setPhone(doctor.getPhone());
            doctorBaseInfoDb.setProfessionScope(doctor.getProfessionScope());
            doctorBaseInfoDb.setTitle(doctor.getTitle());
            doctorBaseInfoDb.setCardNum(doctor.getIdUnm());
            doctorBaseInfoRepository.save(doctorBaseInfoDb);

            if (!ObjectUtils.isEmpty(doctorReq.getRecommend())) {
                Recommend recommend = new Recommend();
                recommend.setDcId(doctorBaseInfoDb.getDcId());
                recommend.setSort(doctorReq.getRecommend().getSort());
                recommend.setCreateTime(new Date());
                recommendRepository.save(recommend);
            }

            Config config = configRepository.findById(doctorBaseInfoDb.getDcId()).orElse(null);
            for (ConfigReqsSave configReq : configReqs) {
                switch (configReq.getAType()) {
                    case 10:
                        config.setNetReferral(configReq.getService());
                        config.setReferralPrice(configReq.getPrice());
                        config.setReferralValid(configReq.getValid() * 60);
                        config.setReferralReception(configReq.getReception());
                        config.setReferralService(configReq.getNotice());
                        break;
                    case 11:
                        config.setImgText(configReq.getService());
                        config.setImgTextPrice(configReq.getPrice());
                        config.setImgTextValid(configReq.getValid() * 60);
                        config.setImgTextReception(configReq.getReception());
                        config.setImgTextService(configReq.getNotice());
                        break;
                    case 12:
                        config.setPhone(configReq.getService());
                        config.setPhonePrice(configReq.getPrice());
                        config.setPhoneValid(configReq.getValid());
                        config.setPhoneReception(configReq.getReception());
                        config.setPhoneService(configReq.getNotice());
                        break;
                    case 13:
                        config.setVideo(configReq.getService());
                        config.setVideoPrice(configReq.getPrice());
                        config.setVideoValid(configReq.getValid());
                        config.setVideoReception(configReq.getReception());
                        config.setVideoService(configReq.getNotice());
                        break;
                }
            }
            configRepository.save(config);

        }

        return map;
    }

    //#region 排班
    @Override
    public List<Map<String, List<Map<String, Object>>>> PlantList(long DCID, Date Month) {

        Calendar ca = Calendar.getInstance();
        ca.setTime(Month);
        ca.set(Calendar.DAY_OF_MONTH, 1);
        Month = ca.getTime();
        ca.add(Calendar.MONTH, 1);
        ca.set(Calendar.DAY_OF_MONTH, 1);
        Date end = ca.getTime();

        List<PlantOrder> list = doctorMapper.getPlantList(DCID, Month, end);
        Map<Date, List<PlantOrder>> dateList = list.stream().collect(Collectors.groupingBy(PlantOrder::getPlantTime));

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        List<Map<String, List<Map<String, Object>>>> date = new ArrayList<>();
        for (Map.Entry<Date, List<PlantOrder>> entry : dateList.entrySet()) {
            Date mapKey = entry.getKey();
            List<PlantOrder> mapValue = entry.getValue();
            List<Map<String, Object>> onePlant = new ArrayList<>();
            mapValue.forEach(m -> {
                Map<String, Object> plant = new HashMap<>();
                plant.put("timeType", m.getTimeType());
                plant.put("info", (m.getTimeType() == 1 ? "上午" : m.getTimeType() == 2 ? "下午" : m.getTimeType() == 3 ? "晚间" : "")
                        + ";共:" + (m.getCountOrder() == 0 ? "无限制" : m.getCountOrder()) + ";售:" + m.getSellCount());
                onePlant.add(plant);
            });

            Map<String, List<Map<String, Object>>> one = new HashMap<>();
            one.put(sdf.format(mapKey), onePlant);
            date.add(one);
        }

        // Map<String, Object> mpa = new HashMap<>();
        // mpa.put("s", detailsMap01);
        return date;
    }

    @Override
    public Boolean DocPlantDel(long DCID, byte timeType, Date Day) {
        List<PlantOrder> dList = plantOrderRepository.getDocPlant(DCID, timeType, Day);
        if (dList.stream().filter(m -> m.getSellCount() > 0).count() > 0) {
            throw new ApiException(400, "排班日期已有预约，不能删除");
        }
        try {
            for (PlantOrder plantOrder : dList) {
                plantOrderRepository.deleteById(plantOrder.getId());
            }
            return true;
        } catch (Exception e) {
        }
        return false;
    }

    @Override
    @JaversAuditable
    @Transactional(rollbackFor = Exception.class)
    public Boolean DocPlantEdit(List<Long> dcIdList, List<Byte> timeTypeList, Boolean isWeekEnd, Date beg, Date end, int countOrder) {
        List<PlantOrder> dList = plantOrderRepository.findAllByDcIdAndTimeType(dcIdList, timeTypeList, beg, end);

        long lbeg = beg.getTime();
        long lend = end.getTime();

        List<PlantOrder> list = new ArrayList<>();
        List<PlantOrder> updlist = new ArrayList<>();
        Date now = new Date();
        for (long dcId : dcIdList) {
            for (long i = lbeg; i <= lend; i += 24 * 60 * 60 * 1000) {
                Date day = new Date(i);
                Calendar cal = Calendar.getInstance();
                cal.setTime(day);
                // 跳过周末
                if (!isWeekEnd && (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)) {
                    continue;
                }
                long lday = i;
                for (Byte tType : timeTypeList) {
                    PlantOrder p = dList.stream().filter(m -> m.getDcId() == dcId && m.getTimeType() == tType && m.getPlantTime().getTime() == lday).findAny().orElse(null);
                    if (p != null) {
                        p.setCountOrder(countOrder == 0 ? 0 : Math.max(countOrder, p.getSellCount()));
                        updlist.add(p);
                    } else {
                        p = new PlantOrder();
                        p.setDcId(dcId);
                        p.setCreateTime(now);
                        p.setPlantTime(day);
                        p.setTimeType(tType);
                        p.setStartTime(tType == 1 ? "08:00" : tType == 2 ? "12:00" : tType == 3 ? "18:00" : "");
                        p.setEndTime(tType == 1 ? "12:00" : tType == 2 ? "18:00" : tType == 3 ? "24:00" : "");
                        p.setCountOrder(countOrder);
                        p.setSellCount(0);
                        p.setDisable(false);
                        list.add(p);
                    }
                }
            }
        }

        if (list.size() == 0 && updlist.size() == 0) {
            throw new ApiException(400, "排班失败，没有有效的排班日期");
        }

        try {
            if (list.size() > 0) {
                plantOrderRepository.saveAll(list);
            }
            if (updlist.size() > 0) {
                plantOrderRepository.saveAll(updlist);
            }
            return true;
        } catch (Exception e) {
            //TODO: handle exception
        }
        return false;
    }
    //#endregion

    /**
     * @param name
     * @param idNum
     * @param phone
     * @return
     */
    private Map<String, String> goToAddUser(String name, String idNum, String phone) {
        // 重新添加ca端的信息
        String addResult = caSendMessage.addTrustedUser(name, "SF", idNum, phone);
        JSONObject addJson = JSONObject.parseObject(addResult);
        Map<String, String> map = new HashMap<>(MapUtils.getMapInitSize(2));
        if ("SUCCESS".equals(addJson.get("message"))) {
            Object data = addJson.get("data");
            JSONObject dataJson = JSONObject.parseObject(data.toString());
            map.put("authCode", dataJson.get("authCode").toString());
            map.put("msspId", dataJson.get("msspId").toString());
            redisUtils.set("ca" + phone + "doctor", dataJson.get("msspId").toString() + ":" + dataJson.get("authCode").toString());
            return map;
        } else {
            // 根据身份证获得
            String idResult = caSendMessage.idNumQueryUserInfo(idNum);
            JSONObject idNumJson = JSONObject.parseObject(idResult);

            if (CaSendMessage.SUCCESS.equals(idNumJson.get(CaSendMessage.MESSAGE))) {
                JSONObject idNumData = JSONObject.parseObject(idNumJson.get("data").toString());
                if (idNumData.get("userStatus").equals(CaSendMessage.NOT_ACTIVE) || idNumData.get("userStatus").equals(CaSendMessage.ACTIVE)) {
                    map.put("msspId", idNumData.get("msspId").toString());
                    if (idNumData.get("userStatus").equals(CaSendMessage.NOT_ACTIVE)){
                        String authCode = caSendMessage.updateAuthCode(idNumData.get("msspId").toString());
                        map.put("authCode", authCode);
                        redisUtils.set("ca" + phone + "doctor", idNumData.get("msspId").toString() + ":" + authCode);
                    }
                    return map;
                } else {
                    throw new ApiException(400, idNumJson.get(CaSendMessage.MESSAGE).toString());
                }
            } else {
                Object message = addJson.get("message");
                throw new ApiException(400, message.toString());
            }

        }
    }
}
